package com.admin.boot.modules.online.service.impl;

import com.admin.boot.common.utils.CodeGenerateUtil;
import com.admin.boot.common.utils.Result;
import com.admin.boot.common.exception.AdminBootOnlineDevException;
import com.admin.boot.common.utils.oConvertUtils;
import com.admin.boot.modules.online.service.IOnlineTableFieldService;
import com.admin.boot.common.utils.FieldType;
import com.admin.boot.modules.online.entity.OnlineTable;
import com.admin.boot.modules.online.entity.OnlineTableField;
import com.admin.boot.modules.online.mapper.OnlineTableMapper;
import com.admin.boot.modules.online.service.IOnlineTableService;
import com.admin.boot.modules.online.vo.CodeGenerateVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @version v3.0
 * @Description: TODO
 * @author: jeecg-boot
 * @date: 2024-01-10
 */
@Slf4j
@Service
public class OnlineTableServiceImpl extends ServiceImpl<OnlineTableMapper, OnlineTable> implements IOnlineTableService {

    @Autowired
    private IOnlineTableFieldService onlineTableFieldService;

    @Override
    @Transactional
    public Result<?> addTable(OnlineTable onlineTable) {
        List<OnlineTableField> tableFields = onlineTable.getTableFields();

        onlineTable.setVersion(1);
        onlineTable.setCreateTime(new Date());
        onlineTable.setSyncStatus(1);
        baseMapper.insert(onlineTable);

        fieldTypeCheck(tableFields);

        for (OnlineTableField tableField : tableFields) {
            tableField.setTableId(onlineTable.getId());
            onlineTableFieldService.save(tableField);
        }
        return Result.OK("添加成功！");
    }

    @Override
    @Transactional
    public Result<?> deleteTable(String id) {
        OnlineTable onlineTable = baseMapper.selectById(id);
        baseMapper.deleteById(id);

        String tableByName = baseMapper.getTableByName(onlineTable.getTableName());
        if (tableByName != null){
            baseMapper.dropTable(onlineTable.getTableName());
        }
        LambdaQueryWrapper<OnlineTableField> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OnlineTableField::getTableId, id);
        onlineTableFieldService.remove(queryWrapper);
        return Result.OK("删除成功！");
    }

    @Override
    @Transactional
    public Result<?> edit(OnlineTable onlineTable) {


        LambdaQueryWrapper<OnlineTableField> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OnlineTableField::getTableId, onlineTable.getId());
        onlineTableFieldService.remove(queryWrapper);

        List<OnlineTableField> tableFields = onlineTable.getTableFields();
        fieldTypeCheck(tableFields);

        for (OnlineTableField tableField : tableFields) {
            tableField.setTableId(onlineTable.getId());
            onlineTableFieldService.save(tableField);
        }

        baseMapper.updateVersion(onlineTable.getId());
        onlineTable.setSyncStatus(1);
        baseMapper.updateById(onlineTable);
        return Result.OK("修改成功！");
    }

    @Override
    @Transactional
    public Result<?> syncDb(String id) {
        OnlineTable onlineTable = baseMapper.selectById(id);
        if (onlineTable.getSyncStatus() == 2) {
            return Result.error("不用重复操作！");
        }
        List<OnlineTableField> fields = onlineTableFieldService.getByTableId(id);

        String tableName = onlineTable.getTableName();
        //查询是否已经建表
        String tableByName = baseMapper.getTableByName(tableName);
        log.info("tableByName:{},正在同步......", tableByName);
        if (StringUtils.isBlank(tableByName)) {
            //创建表
            StringBuilder createSql = new StringBuilder("(");
            for (OnlineTableField field : fields) {
                String s = FieldType.fieldTypeMap.get(field.getFieldType());
                String fieldName = field.getFieldName();
                Integer fieldTypeLength = field.getFieldTypeLength();
                Integer fieldTypeDecimalLength = field.getFieldTypeDecimalLength();
                String fieldDescribe = field.getFieldDescribe();
                String defaultValue = field.getDefaultValue();
                boolean canNull = field.getCanNull();

                createSql.append("`").append(fieldName).append("` ");

                if (FieldType.hasNoLengthFieldTypeList.contains(s)) {
                    createSql.append(s);
                } else {
                    if (FieldType.hasDecimalLengthFieldTypeList.contains(s)) {
                        createSql.append(s).append("(").append(fieldTypeLength).append(",").append(fieldTypeDecimalLength).append(")");
                    } else {
                        createSql.append(s).append("(").append(fieldTypeLength).append(")");
                    }
                }
                if (!canNull) {
                    createSql.append(" not null");
                }
                if (StringUtils.isNotBlank(defaultValue)) {
                    createSql.append(" DEFAULT ").append(defaultValue);
                }
                if (StringUtils.isNotBlank(fieldDescribe)) {
                    createSql.append(" COMMENT ").append("'").append(fieldDescribe).append("'");
                }

                createSql.append(",");
            }
            List<OnlineTableField> collect = fields.stream().filter(OnlineTableField::getIsPrimaryKey).collect(Collectors.toList());
            createSql.append("PRIMARY KEY (");
            for (int i = 0; i < collect.size(); i++) {
                createSql.append("`").append(collect.get(i).getFieldName()).append("`");
                if (i < collect.size() - 1) {
                    createSql.append(",");
                }
            }
            createSql.append("))");
            onlineTable.setSyncStatus(2);
            baseMapper.updateById(onlineTable);
            log.info(createSql.toString());
            baseMapper.createTable(tableName, createSql.toString());
            return Result.OK("同步成功");
        }


        List<Map<String, String>> tableFieldsByTableName = baseMapper.getTableFieldsByTableName(tableName);
        for (Map<String, String> stringStringMap : tableFieldsByTableName) {
            String s = stringStringMap.get("Field");
            //删除不存在的字段
            List<OnlineTableField> collect = fields.stream().filter(field -> field.getFieldName().equals(s)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(collect)) {
                baseMapper.dropColumn(tableName, s);
            }
        }

        //添加或更新字段
        for (OnlineTableField field : fields) {
            log.info("同步字段{}", field);
            String fieldName = field.getFieldName();
            String fieldType = field.getFieldType();
            Integer fieldTypeLength = field.getFieldTypeLength();
            String fieldDescribe = field.getFieldDescribe();
            Integer fieldTypeDecimalLength = field.getFieldTypeDecimalLength();
            String defaultValue = field.getDefaultValue();

            fieldType = FieldType.fieldTypeMap.get(fieldType);

            boolean contains = FieldType.hasNoLengthFieldTypeList.contains(fieldType);
            if (!contains) {
                if (FieldType.hasDecimalLengthFieldTypeList.contains(fieldType)) {
                    fieldType = fieldType + "(" + fieldTypeLength + "," + fieldTypeDecimalLength + ")";
                } else {
                    fieldType = fieldType + "(" + fieldTypeLength + ")";
                }
            }
            boolean hasField = false;
            for (Map<String, String> stringStringMap : tableFieldsByTableName) {

                String s = stringStringMap.get("Field");
                if (fieldName.equals(s)) {
                    hasField = true;
                    baseMapper.updateTableField(tableName, fieldName, fieldType, fieldDescribe, defaultValue);
                    break;
                }
            }
            //添加字段
            if (!hasField) {
                baseMapper.addField(tableName, fieldName, fieldType, fieldDescribe, defaultValue);
            }
        }
        onlineTable.setSyncStatus(2);
        baseMapper.updateById(onlineTable);
        log.info("tableByName:{},同步成功......", tableByName);
        return Result.OK("同步成功");
    }

    @Override
    public void codeGenerate(CodeGenerateVo codeGenerateVo) throws IOException {


        OnlineTable byId = this.getById(codeGenerateVo.getId());

        List<String> codeModules = codeGenerateVo.getCodeModules();
        String packageName = codeGenerateVo.getPackageName();
        String entityName = codeGenerateVo.getEntityName();
        String tableName = codeGenerateVo.getTableName();
        String position = codeGenerateVo.getPosition();

        List<OnlineTableField> byTableId = onlineTableFieldService.getByTableId(byId.getId());


        HashMap<String, Object> data = new HashMap<>();
        data.put("packageName", packageName);
        data.put("entityName", entityName);
        data.put("tableName", tableName);
        data.put("tableDescribe",byId.getTableDescribe());

        packageName = packageName.replaceAll("\\.", "\\\\");
        String dir = position + "\\" + packageName;

        for (String codeModule : codeModules) {

            switch (codeModule) {
                case "controller":
                    String filPath = dir + "\\controller";
                    File file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    String fileName = filPath + "\\" + entityName + "Controller.java";
                    File file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }
                    //生成controller代码
                    CodeGenerateUtil.codeGenerate(fileName, data, "controller.ftl");
                    break;
                case "service":
                    filPath = dir + "\\service";
                    file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    fileName = filPath + "\\" + "I" + entityName + "Service.java";
                    file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }
                    //service
                    CodeGenerateUtil.codeGenerate(fileName, data, "service.ftl");
                    break;
                case "serviceImpl":
                    filPath = dir + "\\service\\impl";
                    file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    fileName = filPath + "\\" + entityName + "ServiceImpl.java";
                    file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }
                    //serviceImpl
                    CodeGenerateUtil.codeGenerate(fileName, data, "serviceImpl.ftl");
                    break;
                case "mapper":
                    filPath = dir + "\\mapper";
                    file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    fileName = filPath + "\\" + entityName + "Mapper.java";
                    file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }
                    //mapper
                    CodeGenerateUtil.codeGenerate(fileName, data, "mapper.ftl");
                    break;
                case "mapper.xml":
                    filPath = dir + "\\mapper\\xml";
                    file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    fileName = filPath + "\\" + entityName + "Mapper.xml";
                    file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }
                    //mapper.xml
                    CodeGenerateUtil.codeGenerate(fileName, data, "mapper.xml.ftl");
                    break;
                case "entity":
                    filPath = dir + "\\entity";
                    file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    fileName = filPath + "\\" + entityName + ".java";
                    file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }

                    List<OnlineTableField> collect = byTableId.stream().peek(item -> item.setFieldName(oConvertUtils.str2Hump(item.getFieldName()))).collect(Collectors.toList());
                    List<String> collect1 = byTableId.stream().map(OnlineTableField::getFieldType).distinct().collect(Collectors.toList());
                    data.put("fields",collect);
                    data.put("fieldTypes",collect1);
                    //mapper.xml
                    CodeGenerateUtil.codeGenerate(fileName, data, "entity.ftl");
                    break;
                case "vue":
                    filPath = dir + "\\vue";
                    file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    fileName = filPath + "\\" + entityName + ".vue";
                    file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }
                    data.remove("entityName");
                    data.put("entityName",oConvertUtils.First2LowerCase(entityName));

                    collect = byTableId.stream().peek(item -> item.setFieldName(oConvertUtils.str2Hump(item.getFieldName()))).collect(Collectors.toList());
                    data.put("fields",collect);

                    CodeGenerateUtil.codeGenerate(fileName, data, "vue.ftl");
                case "router":
                    filPath = dir + "\\vue";
                    file = new File(filPath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    fileName = filPath + "\\" + entityName +"Router"+ ".txt";
                    file1 = new File(fileName);
                    if (!file1.exists()) {
                        file1.createNewFile();
                    }
                    data.remove("entityName");
                    data.put("entityName",oConvertUtils.First2LowerCase(entityName));
                    CodeGenerateUtil.codeGenerate(fileName, data, "router.ftl");
            }
        }


    }


    private void fieldTypeCheck(List<OnlineTableField> fields) {
        for (OnlineTableField field : fields) {
            String fieldType = field.getFieldType();
            if (StringUtils.isEmpty(fieldType)) {
                throw new AdminBootOnlineDevException("字段类型错误！");
            }
            String s = FieldType.fieldTypeMap.get(field.getFieldType());
            if (StringUtils.isEmpty(s)) {
                throw new AdminBootOnlineDevException("字段类型错误！");
            }

            Integer maxLength = FieldType.fieldTypeLengthMap.get(fieldType);
            if (maxLength != null) {
                Integer fieldTypeLength = field.getFieldTypeLength();
                if (fieldTypeLength == null || fieldTypeLength > maxLength || fieldTypeLength <= 0) {
                    throw new AdminBootOnlineDevException("字段类型长度错误！");
                }
            }

            Integer i = FieldType.fieldTypeDecimalLengthMap.get(fieldType);
            if (i != null) {
                Integer fieldTypeDecimalLength = field.getFieldTypeDecimalLength();
                if (fieldTypeDecimalLength == null || fieldTypeDecimalLength > i || fieldTypeDecimalLength < 0) {
                    throw new AdminBootOnlineDevException("字段类型长度错误！");
                }
            }
        }
    }
}
